import inspect
import re
import time

from loguru import logger

from common import utils


class ADB(object):
    platform_name = utils.get_platform_name()

    def __init__(self, ip, port=5555):
        self.ip = ip
        self.port = port

    def adb_connect(self, timeout=5):
        cmd = 'adb connect {}:{}'.format(self.ip, self.port)
        cmd_result = utils.get_execute_cmd_result(cmd, timeout=timeout)
        if 'connected to {}'.format(self.ip) not in cmd_result:
            raise Exception('adb connect failed, err={}'.format(cmd_result))
        self._generate_command_log()
        return cmd_result

    def adb_can_connect(self, timeout=5) -> bool:
        try:
            self.adb_connect(timeout)
            return True
        except Exception as e:
            logger.warning("adb can't connect {}, err={}".format(self.ip, str(e)))
            return False

    def open_activity_with_url(self, package_name, activity_name, url, timeout=10):
        cmd = 'adb -s {} shell am start -n {}/{} -d "{}"'.format(
            self.ip, package_name, activity_name, url)
        utils.get_execute_cmd_result(cmd, timeout=timeout)
        self._generate_command_log()

    def kill_package(self, package_name, timeout=10):
        cmd = 'adb -s {} shell am force-stop "{}"'.format(self.ip, package_name)
        utils.get_execute_cmd_result(cmd, timeout=timeout)
        self._generate_command_log()

    def check_adb_is_connect(self, timeout=10) -> bool:
        devices = self.list_adb_devices(timeout)
        self._generate_command_log()
        return bool([device for device in devices if self.ip in device])

    @staticmethod
    def list_adb_devices(timeout=10):
        """"
        List of devices attached
        192.168.2.243:5555	device
        :return: 192.168.2.243:5555
        """
        cmd = 'adb devices'
        result = utils.get_execute_cmd_result(cmd, timeout=timeout)
        p = result.split('\n')[1:]
        devices = [str1.split('\t')[0] for str1 in p]
        logger.info('adb devices\n\t{}'.format(devices))
        return devices

    def wait_for_device(self, timeout=10):
        cmd = "adb -s {} wait-for-device".format(self.ip)
        utils.get_execute_cmd_result(cmd, timeout=timeout)
        self._generate_command_log()

    def _generate_command_log(self):
        func_name = inspect.stack()[1][3]
        logger.debug("{} {} success".format(self.ip, func_name))

    def tap(self, x, y):
        self.wait_for_device()
        cmd = "adb -s {} shell input tap {} {} ".format(self.ip, x, y)
        utils.get_execute_cmd_result(cmd)
        self._generate_command_log()

    def dump_xml(self, path, timeout=10):
        self.wait_for_device()
        cmd = "adb -s {} shell uiautomator dump {}".format(self.ip, path)
        output = utils.get_execute_cmd_result(cmd, timeout=timeout)
        logger.debug('{}\n\tshare: {}'.format(cmd, output))
        self._generate_command_log()

    def download_file(self, src, dst, timeout=10):
        self.wait_for_device()
        cmd = "adb -s {} pull {} {}".format(self.ip, src, dst)
        output = utils.get_execute_cmd_result(cmd, timeout=timeout)
        logger.debug('{}\n\tshare: {}'.format(cmd, output))
        self._generate_command_log()

    def input_text(self, text, timeout=10):
        self.wait_for_device()
        cmd = "adb -s {} shell input text {}".format(self.ip, text)
        utils.get_execute_cmd_result(cmd, timeout=timeout)
        logger.debug('{} success'.format(cmd))
        self._generate_command_log()

    def check_screen_is_locked(self, timeout=10):
        self.wait_for_device()
        map_dict = {'windows': 'findstr', 'linux': 'grep'}
        map_string = map_dict[ADB.platform_name]
        cmd = "adb -s {} shell dumpsys window policy | {} isStatusBarKeyguard".format(
            self.ip, map_string)
        output = utils.get_execute_cmd_result(cmd, timeout=timeout)
        locked_status = re.findall(r"isStatusBarKeyguard=(\w+)", output)[0]
        logger.debug('{}\n\tshare: screen_is_locked={}'.format(cmd, locked_status))
        self._generate_command_log()
        return locked_status == 'true'

    def light_up_screen(self, timeout=10):
        cmd = 'adb -s {} shell input keyevent 224'.format(self.ip)
        utils.get_execute_cmd_result(cmd, timeout=timeout)
        self._generate_command_log()

    def swipe_to_unlock(self, timeout=10):
        cmd = 'adb -s {} shell input swipe 300 1000 300 500'.format(self.ip)
        utils.get_execute_cmd_result(cmd, timeout=timeout)
        self._generate_command_log()

    def swipe_to_show_notification(self, timeout=10):
        cmd = 'adb  -s {} shell input swipe 300 0 300 1500'.format(self.ip)
        utils.get_execute_cmd_result(cmd, timeout=timeout)
        self._generate_command_log()

    def unlock_phone(self):
        if self.check_screen_is_locked():
            self.light_up_screen()
            time.sleep(1)
        self.swipe_to_unlock()
        time.sleep(1)

    def get_notification(self, timeout=10):
        if utils.check_str_equal(ADB.platform_name, "Windows", match_case=False):
            notification_path = utils.get_abs_path('share', 'notification.txt')
            download_notification_cmd = 'adb -s {} shell dumpsys notification > {}'.format(
                self.ip, notification_path)
            utils.get_execute_cmd_result(download_notification_cmd, timeout=timeout)
            pattern = re.compile(r'tickerText=(.*)')
            # pattern = re.compile(r"NotificationRecord(.*)pkg=(.*)")
            content = utils.read_file(notification_path)
            result = pattern.findall(content)
            result = [item for item in result if item != 'null']
        elif utils.check_str_equal(ADB.platform_name, "LINUX", match_case=False):
            cmd = "adb -s {} shell dumpsys notification |grep tickerText | grep  -v 'null'".format(self.ip)
            result = utils.get_execute_cmd_result(cmd)
        else:
            raise Exception('not support this system={}'.format(ADB.platform_name))
        if isinstance(result, list):
            result = ';'.join(result)
        logger.debug('手机通知={}'.format(result))
        return result

    def get_all_notification_package(self, timeout=10):
        map_dict = {'windows': 'findstr', 'linux': 'grep'}
        map_string = map_dict[ADB.platform_name.lower()]
        cmd = 'adb -s {} shell dumpsys notification | {} NotificationRecord'.format(self.ip, map_string)
        result = utils.get_execute_cmd_result(cmd, timeout=timeout)
        if isinstance(result, list):
            result = ';'.join(result)
        pattern = re.compile(r'(?<=pkg=).*?(?=\s)')
        package_list = pattern.findall(result)
        logger.debug('通知包名={}'.format(package_list))
        return package_list

    def check_this_package_have_notification(self, package_name):
        package_list = self.get_all_notification_package()
        flag = package_name in package_list
        logger.debug('if_have_notification={}'.format(flag))
        return flag

    def check_package_name_running(self, package_name, timeout=10):
        map_dict = {'windows': 'findstr', 'linux': 'grep'}
        map_string = map_dict[ADB.platform_name.lower()]
        cmd = 'adb -s {} shell dumpsys activity activities | {} packageName'.format(
            self.ip, map_string)
        result = utils.get_execute_cmd_result(cmd, timeout=timeout)
        if isinstance(result, list):
            result = ';'.join(result)
        flag = package_name in result
        logger.debug('{}包名是否在运行={}'.format(package_name, flag))
        return flag

    def open_activity(self, package_name, activity_name, timeout=10):
        cmd = 'adb -s {} shell am start {}/{}'.format(
            self.ip, package_name, activity_name)
        utils.get_execute_cmd_result(cmd, timeout=timeout)
        self._generate_command_log()

    def check_app_running_health(self, package_name, activity_name, timeout=10):
        if not self.check_package_name_running(package_name):
            logger.warning('{} is not running'.format(package_name))
            self.unlock_phone()
            cmd = 'adb -s {} shell am start {}/{}'.format(
                self.ip, package_name, activity_name)
            utils.get_execute_cmd_result(cmd, timeout=timeout)
            logger.info('{} restart'.format(package_name))
        else:
            logger.debug('{} is running'.format(package_name))


def check_ips_can_adb_connect(android_ips):
    result = {"err_flag": False, "value": {}}
    for ip in android_ips:
        try:
            m = ADB(ip).adb_connect()
        except Exception as e:
            result["err_flag"] = True
            result["value"][ip] = str(e)
        else:
            result["value"][ip] = m
    return result
